package sk.stuba.fiit.clientApp.service.impl;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.BasicConstraints;

import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import sk.stuba.fiit.clientApp.service.CertificateService;
import sk.stuba.fiit.clientApp.util.InitializationUtil;
import sk.stuba.fiit.clientApp.ws.client.CertificateServiceClient;
import sk.stuba.fiit.pki.ws.schema.beans.CertificateRequest;
import sk.stuba.fiit.pki.ws.schema.beans.CertificateResponse;
import sk.stuba.fiit.pki.ws.schema.beans.CertificateType;
import sun.misc.BASE64Decoder;

@SuppressWarnings("deprecation")
@Service("certificateService")
public class CertificateServiceImpl implements CertificateService {
	
	@Autowired
	private CertificateServiceClient certificateServiceClient;

	private  BigInteger allocateSerialNumber() throws IOException {
		File file = new File("serial.txt");
		if (!file.exists())
			file.createNewFile();
		DataInputStream dis = new DataInputStream(new FileInputStream(file));
		int cislo;
		try {
			cislo = dis.readInt();
		} catch (Exception e) {
			cislo = 1;
		} finally {
			dis.close();
		}
		cislo++;
		System.out.println(cislo);
		DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
		dos.writeInt(cislo);
		dos.close();
		return new BigInteger("" + cislo);
	}

	@Override
	public  X509Certificate generateRootSelfSignedCertificate(KeyPair pair)
			throws Exception {
		X509V3CertificateGenerator x509v3certificategenerator = new X509V3CertificateGenerator();
		x509v3certificategenerator.setSerialNumber(allocateSerialNumber());
		x509v3certificategenerator.setIssuerDN(new X509Name(InitializationUtil.getCertificateProperties().getProperty("R_setIssuerDN")));
		x509v3certificategenerator.setNotBefore(new Date(System.currentTimeMillis()));
		int tmp = Integer.valueOf(InitializationUtil.getCertificateProperties().getProperty("R_setNotAfter"));
		x509v3certificategenerator.setNotAfter(new Date(System.currentTimeMillis()	+ tmp*24*60*60*1000));
		x509v3certificategenerator.setSubjectDN(new X509Name(InitializationUtil.getCertificateProperties().getProperty("R_setSubjectDN")));
		x509v3certificategenerator.setPublicKey(pair.getPublic());
		x509v3certificategenerator.setSignatureAlgorithm(InitializationUtil.getCertificateProperties().getProperty("R_setSignatureAlgorithm"));		
		x509v3certificategenerator.addExtension(X509Extensions.BasicConstraints, false,	new BasicConstraints(true));
		X509Certificate x509certificate = x509v3certificategenerator.generate(pair.getPrivate(),"BC");
		x509certificate.checkValidity(new Date());
		x509certificate.verify(pair.getPublic());
		return x509certificate;
	}
	
	@Override
	public  X509Certificate generateCertificateAuthorityCertificate(PublicKey intKey, PrivateKey caKey, X509Certificate caCert) throws Exception{
		  	X509V3CertificateGenerator x509v3certificategenerator = new X509V3CertificateGenerator();
		      x509v3certificategenerator.setSerialNumber(allocateSerialNumber());
		      x509v3certificategenerator.setIssuerDN(caCert.getSubjectX500Principal());
		      x509v3certificategenerator.setNotBefore(new Date(System.currentTimeMillis()));
		      int tmp = Integer.valueOf(InitializationUtil.getCertificateProperties().getProperty("CA_setNotAfter"));
		      x509v3certificategenerator.setNotAfter( new Date(System.currentTimeMillis() + tmp*24*60*60*1000));
		      x509v3certificategenerator.setSubjectDN( new X509Name(InitializationUtil.getCertificateProperties().getProperty("CA_setSubjectDN")));
		      x509v3certificategenerator.setPublicKey(intKey);
		      x509v3certificategenerator.setSignatureAlgorithm(InitializationUtil.getCertificateProperties().getProperty("CA_setSignatureAlgorithm"));
		      x509v3certificategenerator.addExtension(X509Extensions.AuthorityKeyIdentifier,false, new AuthorityKeyIdentifierStructure(caCert));
		      x509v3certificategenerator.addExtension(X509Extensions.SubjectKeyIdentifier,false, new SubjectKeyIdentifierStructure(intKey));
	 	      x509v3certificategenerator.addExtension(X509Extensions.BasicConstraints,true, new BasicConstraints(0));
	 	     
//	 	      TODO pridat distribucny bod
//	 	      DistributionPoint[] adistributionpoint = new DistributionPoint[]{new DistributionPoint(new DistributionPointName(new GeneralNames(0,"DSDS")),null,)};
//			x509v3certificategenerator.addExtension(X509Extensions.CRLDistributionPoints, false, new CRLDistPoint(adistributionpoint ));
	     
	 	      x509v3certificategenerator.addExtension( X509Extensions.KeyUsage, true, new KeyUsage( KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign));
			x509v3certificategenerator.addExtension(X509Extensions.ExtendedKeyUsage, true,new ExtendedKeyUsage(new DERSequence(KeyPurposeId.id_kp_timeStamping)));			
			X509Certificate newCert = x509v3certificategenerator.generateX509Certificate(caKey, "BC");
			newCert.checkValidity(new Date());
			newCert.verify(caCert.getPublicKey());
			return newCert;
	}
	
	
	@Override
	public  X509Certificate generateEndEntityCert(PublicKey entityKey, PrivateKey caKey, X509Certificate caCert)  throws Exception
		   {
		      X509V3CertificateGenerator x509v3certificategenerator = new X509V3CertificateGenerator();

		      x509v3certificategenerator.setSerialNumber(allocateSerialNumber());
		      x509v3certificategenerator.setIssuerDN(caCert.getSubjectX500Principal());
		      x509v3certificategenerator.setNotBefore(new Date(System.currentTimeMillis()));
		      int tmp= Integer.valueOf(InitializationUtil.getCertificateProperties().getProperty("E_setNotAfter"));
		      x509v3certificategenerator.setNotAfter(new Date(System.currentTimeMillis() + tmp*24*60*60*1000));
		      x509v3certificategenerator.setSubjectDN(new X509Name(InitializationUtil.getCertificateProperties().getProperty("E_setSubjectDN")));
		      x509v3certificategenerator.setPublicKey(entityKey);
		      x509v3certificategenerator.setSignatureAlgorithm(InitializationUtil.getCertificateProperties().getProperty("E_setSignatureAlgorithm"));
		      x509v3certificategenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));
		      x509v3certificategenerator.addExtension(X509Extensions.SubjectKeyIdentifier,false, new SubjectKeyIdentifierStructure(entityKey));
		      x509v3certificategenerator.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
		      x509v3certificategenerator.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
		      X509Certificate newCert = x509v3certificategenerator.generateX509Certificate(caKey, "BC");
			newCert.checkValidity(new Date());
			newCert.verify(caCert.getPublicKey());
			return newCert;
		   }

	@Override
	public X509Certificate getCertificateBySerialNum(String serialNum) throws Exception {
		CertificateRequest request = new CertificateRequest();
		CertificateType certType = new CertificateType();
		certType.setSerialNumber(serialNum);
		request.setCertificatereq(certType );
		CertificateResponse response= certificateServiceClient.getCertificateBySerialNumber(request);
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] certData = decoder.decodeBuffer(response.getCertificateresp().get(0));
		CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
		InputStream in = new ByteArrayInputStream(certData);		
		X509Certificate certificate = (X509Certificate)certFactory.generateCertificate(in);
		return certificate;
	}

	@Override
	public X509Certificate getCertificateByIssuer(String string) throws Exception {
		CertificateRequest request = new CertificateRequest();
		CertificateType certType = new CertificateType();
		certType.setIssuer(string);
		certType.setSerialNumber(null);
		request.setCertificatereq(certType );
		CertificateResponse response= certificateServiceClient.getCertificateBySerialNumber(request);
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] certData = decoder.decodeBuffer(response.getCertificateresp().get(0));
		CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
		InputStream in = new ByteArrayInputStream(certData);		
		X509Certificate certificate = (X509Certificate)certFactory.generateCertificate(in);
		return certificate;
	}

}
